#!/usr/bin/python
import sys, os

def usage():
    print("Generate snippets for parsing command line in different language")
    print
    print("  argParser [python|cpp|tclap|bash] template_file")
    print
    print("Note, this tool is to help you quick generated arguments parsing procedure.")
    print("I suggest you to implement usage() function in all cases ")
    print("or feel free to modify the output file")

def readTemplate(file):
    ret = list()
    lineNo = 0
    for i in open(file):
        lineNo = lineNo + 1
        if (i[0] == '#' or len(i.strip()) == 0 ):
            continue
        splits = i.strip().split()
        if (len(splits) != 6):
            print "Wrong synax at line %d ignored. " % lineNo
        ret.append(dict(zip(('name','type','default','short','long','numArgs'),splits)))
    return ret

def genPython(template):
    # set default values
    print "##############################"
    print "#  Generated by argParser    "
    print "#  %d arguments are processed. " % len(template)
    print "########### BEGIN  ###########"
    print "import getopt"
    for i in template:
        print "%s = %s(%s)" % (i["name"], i["type"], i["default"])
    
    def notNone(x):
        if x != None:
            return True
        return False
    def getShortOpt(x):
        if (x["short"] == "None"):
            return None
        if (x["type"] != "bool"):
            return x["short"][1:]+":"
        else:

            return x["short"][1:]
    shortOptString = "\"" + "".join(filter(notNone, [ getShortOpt(i) for i in template]))+"\""
    #print shortOptString
   
    def getLongOpt(x):
        if (x["long"] == "None"):
            return None
        if (x["type"] != "bool"):
            return "\""+x["long"][2:]+"=\""
        else:

            return "\""+x["long"][2:]+"\""
    longOptString = "[" + ",".join(filter(notNone, [ getLongOpt(i) for i in template])) + "]"
    #print longOptString
    print "try:"
    print "    _opts, remainArgs = getopt.getopt(sys.argv[1:], %s, %s )" % (shortOptString, longOptString)
    print "except getopt.GetoptError, err:"
    print "    # print help information and exit:"
    print "    print str(err) # will print something like \"option -a not recognized\""
    print "    usage()"
    print "    sys.exit(2)"
    print "for o, a in _opts:"
    print "    if o == None:"
    print "        pass"
    
    for i in template:
        print "    elif o in (\"%s\", \"%s\"):" % (i["short"], i["long"])
        if (i["type"] != "bool"):
            print "        %s = %s(a)" % (i["name"] , i["type"])
        else:
            print "        %s = True" % i["name"]
    print "    else:"
    print "        assert False, \"unhandled option\""

    print "############ END ############"
    return

def genCPP(template):
    # set default values
    print "//##############################"
    print "//#  Generated by argParser    "
    print "//#  %d arguments are processed. " % len(template)
    print "//########### BEGIN  ###########"
    print "// remember to add "
    print "//#include <vector>"
    print "//#include <string>"
    print "//#include <string.h>"
    print "int _c = 0; "
    for i in template:
        print "%s %s; %s = %s;" % (i["type"], i["name"], i["name"], i["default"])
    print "std::vector<std::string> remainArgs; "
    def notNone(x):
        if x != None:
            return True
        return False
    def getShortOpt(x):
        if (x["short"] == "None"):
            return None
        if (x["type"] != "bool"):
            return x["short"][1:]+":"
        else:

            return x["short"][1:]
    shortOptString = "\"" + "".join(filter(notNone, [ getShortOpt(i) for i in template]))+"\""
    #print shortOptString
    shortOptVar = [ i for i in template if i["short"] != "None"]
    def getLongOpt(x):
        if (x["long"] == "None"):
            return None
        if (x["type"] != "bool"):
            return "{\""+x["long"][2:]+"\", 1, 0, 0}"
        else:

            return "{\""+x["long"][2:]+"\", 0, 0, 0}"
    longOptString = "static struct option long_options[] = {" + \
        ",".join(filter(notNone, [ getLongOpt(i) for i in template])) + ",{0,0,0,0}};"
    #print longOptString
    longOptVar = [ i for i in template if i["long"] != "None"]

    convertFunc = {"bool":"", "int":"atoi", "long":"atol", "float":"atof"}

    print "while (1) {"
    print "    int this__option_optind = optind ? optind : 1;" 
    print "    int option_index = 0;"
    print "    %s" % longOptString;
    print "    _c = getopt_long(argc, argv, %s, long_options, &option_index); " % shortOptString
    print "    if (_c == -1)"
    print "        break;"
    print "    switch (_c) {"
    print "    case 0:"
    # loop long parameters
    for v in longOptVar:
        print "        if (strcmp(long_options[option_index].name, \"%s\") == 0) { if (!long_options[option_index].has_arg) %s = true; else %s = %s(optarg); }" % (v["long"][2:], v["name"], v["name"], convertFunc[v["type"]])
    print "        break;"
    # loop short parameters
    for v in shortOptVar:
        print "    case \'%s\': " % v["short"][1:]
        if v["type"] == "bool":
            print "        %s = true; " % v["name"]
        elif v["type"] in convertFunc.keys():
            print "        %s = %s(optarg); " % (v["name"], convertFunc[v["type"]])
        else:
            print "        %s = %s(optarg); " % (v["name"], v["type"])

        print "        break; "

    print "    default:"
    print "        printf(\"?? getopt_long returned character code 0%o ??\\n\", _c);"
    print "    }"
    print "}"

    print "if (optind < argc) {"
    print "    while (optind < argc)"
    print "        remainArgs.push_back(argv[optind++]);"
    print "}"
    print ""
    print "//############ END ############"
    return

def genTCLAP(template):
    for i in template:
        print "%s %s; %s = %s;" % (i["type"], i["name"], i["name"], i["default"])
    print "std::vector<std::string> remainArgs; "

    print "try {  "
    print "    TCLAP::CmdLine cmd(\"Customized description message\", ' ', \"0.0\");"

    ValueArg = list()
    SwitchArg = list()

    def trimArgument(arg):
        if (arg["short"] == "None"):
            arg["short"] = ""
        if (arg["long"] == "None"):
            arg["long"] = ""
        return arg
    for i in template:
        if (i["type"] == "bool"):
            SwitchArg.append(trimArgument(i))
        else:
            ValueArg.append(trimArgument(i))

    def getUsageString(arg):
        ret = "Argument: "
        if len(arg["short"]) > 0:
            ret = ret + "[" + arg["short"] + "] "
        if len(arg["long"]) > 0:
            ret = ret + "[" + arg["long"] + "] "
        return "\"" + ret + "\""
    counter = 0
    for i in SwitchArg:
        if (i["short"] == "None"):
            i["short"] = ""
        if (i["long"] == "None"):
            i["long"] = ""
        print "    TCLAP::SwitchArg reverseSwitch%d(\"%s\",\"%s\", %s , cmd, %s);" % (counter, i["short"][1:], i["long"][2:], getUsageString(i), i["default"])
        counter = counter + 1
    for i in ValueArg:
        if (i["short"] == "None"):
            i["short"] = ""
        if (i["long"] == "None"):
            i["long"] = ""
        print "    TCLAP::ValueArg<%s> nameArg%d (\"%s\",\"%s\", %s , false, %s, %s, cmd); " % (i["type"], counter, i["short"][1:], i["long"][2:], getUsageString(i), i["default"], getUsageString(i));
        counter = counter + 1

    print "    TCLAP::UnlabeledMultiArg<std::string> _remain(\"remains\", \"Remaining arguments\", false, \"remainingArgs\", cmd);"
    print "    cmd.parse( argc, argv );"

    counter = 0
    for i in SwitchArg:
        print "    %s = reverseSwitch%d.getValue();" % (i["name"], counter)
        counter = counter + 1
    for i in ValueArg:
        print "    %s = nameArg%d.getValue();" % (i["name"], counter)
        counter = counter + 1
    print "    remainArgs = _remain.getValue();"

    print "} catch (TCLAP::ArgException &e)  // catch any exceptions"
    print "{ std::cerr << \"Error: \" << e.error() << \" for arg \" << e.argId() << std::endl; }"
    
    return

def genBash(template):
    print "Unfinished!"
    return

if __name__ == '__main__':
    if (len(sys.argv) != 3):
        usage()
        exit(1)
    template = readTemplate(sys.argv[2])
    if (len(template) == 0):
        print "No arguments found in template file: %s" 
    availableMethod = {"python":genPython, 
                       "cpp":genCPP,
                       "tclap":genTCLAP,
                       "bash":genBash}
    if sys.argv[1] in availableMethod:
        availableMethod[sys.argv[1]](template)
    else:
        print "Unsupported language"
